﻿using EmbeddedSharp.Core;
using EmbeddedSharp.Core.Function.Arithmetics;
using RootFinding;

namespace Zove.Runtime.Core.InferencingStrategy
{
    /// <summary>
    /// Equation inferencing strategy
    /// </summary>
    public class EquationInferencingStrategy : AbstractInferencingStrategy
    {
        protected ILanguageNature LanguageNature;

        public EquationInferencingStrategy(ILanguageNature languageNature)
        {
            LanguageNature = languageNature;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="zoveObject"></param>
        public override void Infer(ZoveObject zoveObject)
        {
            bool updated;

            do
            {
                updated = false;
                foreach (RuntimeEquation r in zoveObject.Model.Equations)
                {
                    foreach (RuntimeFactor e in r.Factors)
                    {
                        if (e.Status != RuntimeFactorStatus.Unknown)
                        {
                            continue;
                        }

                        RuntimeRule rr = zoveObject.Model.GetRuntimeRule(r, e);
                        if (rr.Root == null)
                        {
                            bool success = true;
                            foreach (RuntimeFactor rff in r.Factors)
                            {
                                if (e == rff)
                                {
                                    continue;
                                }

                                if (rff.Status == RuntimeFactorStatus.Unknown)
                                {
                                    success = false;
                                    break;
                                }
                            }
                            if (success)
                            {
                                updated = true;
                                var se = new SolvedFactor();
                                se.Strategy = this;
                                se.Factor = e;
                                se.Rule = rr;

                                Evaluate(se);
                                e.Solve(rr);

                                zoveObject.Queue[e.Name] = se;
                            }
                        }
                    }
                }
            } while (updated);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sf"></param>
        /// <returns></returns>
        public override double Evaluate(SolvedFactor sf)
        {
            RuntimeEquation r = sf.Rule.Equation;
            var node = new SubNode();
            node.Left = r.Left;
            node.Right = r.Right;

            ZoveUnaryFunctionTranslator ef = new ZoveUnaryFunctionTranslator(
                    LanguageNature.FunctionManager, sf.Factor.Name);
            string ne = ef.Translate(node);
            sf.Factor.Value = Evaluate(ne);

            return sf.Factor.Value;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expr"></param>
        /// <returns></returns>
        public double Evaluate(string expr)
        {
            IRootFinder rootFinder = new SimpleRootFinder();
            double result = rootFinder.Solve(expr, 0, double.MaxValue);
            return result;
        }
    }
}